package com.xzq.proxy;

import com.xzq.client.NettyClient;
import com.xzq.client.NettyMessageHandler;
import com.xzq.message.RpcRequestMessage;
import com.xzq.message.RpcResponseMessage;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.util.concurrent.DefaultPromise;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author xzq
 * @Description //TODO
 * @Date 2021/11/22 21:48
 * @Version 1.0.0
 **/
public class ProxyFactory {

    private static Map<String, Channel> channelManager = new ConcurrentHashMap<>();

    public static <T> T getProxy(Class<T> target,String host,int port) {
        Channel channel = channelManager.get(host+":"+port);
        if (channel == null) {
            NettyClient nettyClient = new NettyClient();
            InetSocketAddress address = new InetSocketAddress(host, port);
            ChannelFuture f = nettyClient.connect(address);
            new Thread(() -> {
                try {
                    f.channel().closeFuture().sync();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            channel = f.channel();
            channelManager.put(host+":"+port, channel);
        }
        ClassLoader classLoader = target.getClassLoader();
        Class[] interfaces = {target};
        //JDK动态代理
        return (T) Proxy.newProxyInstance(classLoader, interfaces, new proxyHandler(target,channel));
    }

    public static class proxyHandler implements InvocationHandler{
        private Class target;
        private Channel channel;
        public proxyHandler(Class target,Channel channel) {
            this.target = target;
            this.channel = channel;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //构建RpcRequestMessage
            RpcRequestMessage msg = new RpcRequestMessage(
                    target.getName(),
                    method.getName(),
                    method.getParameterTypes(),
                    args,
                    method.getReturnType()
            );
            //发送消息
            ChannelFuture channelFuture = channel.writeAndFlush(msg);
            channelFuture.addListener(e -> {
                if (!e.isSuccess()) {
                    e.cause();
                }
            });
            System.out.println("发送消息");
            //使用Promise对象 接受结果
            DefaultPromise<Object> promise = new DefaultPromise<>(channel.eventLoop());
            NettyMessageHandler.PROMISES.put(channel.id(), promise);

            //同步等待promise结果
            promise.await();
            if (promise.isSuccess()) {
                return promise.getNow();
            }else{
                //调用失败
                throw new RuntimeException(promise.cause());
            }
        }
    }
}
